// Copyright 2007 the original author or authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package lichen.internal.services;

import java.util.List;

import lichen.entities.forum.Treeable;
import lichen.internal.utils.ClazzUtils;

import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Restrictions;
import org.springframework.orm.hibernate3.HibernateTemplate;

/**
 * 抽象对树的移动进行的处理
 * 
 * @author <a href="mailto:jun.tsai@bjmaxinfo.com">Jun Tsai</a>
 * @version $Revision: 191 $
 * @since 0.0.1
 */
abstract class AbstractMoveTreeNodeProcessor {
	// 隔离元素的HQL
	private final static String ISOLATE_NODE_HQL = "update %s  t set t.left=0 - t.left,t.right=0 - t.right  where (t.left between ? and ?) ";

	// 更新受影响的节点
	private final static String UPDATE_NODES_AFFECTED = "update %1$s  t set t.left=t.left+%2$d,t.right=t.right+%3$d  where (t.left between ? and ?) ";

	// 更新当前的NODE
	private final static String UPDATE_CURRENT_NODE_HQL = "update %1$s  t set t.left=%2$d-t.left ,t.right=%2$d-t.right  where t.left <?  ";

	// 父节点
	private Treeable parentNode;

	// 当前操作的节点
	private Treeable node;

	
	

	// 对应的节点类的名字
	private String treeClassName;

	//Hiberante模板类
	private HibernateTemplate _template;

	/**
	 * 构造跟节点对象
	 * 
	 * @return 根节点对象
	 */
	protected abstract Treeable constructRootNode();

	/**
	 * 得到当前的节点
	 * 
	 * @return 当前的节点
	 */
	public Treeable getCurrentNode() {
		return this.node;
	}

	/**
	 * 得到操作树的类名
	 * 
	 * @return 树的类名
	 */
	protected String getTreeClassName() {
		return this.treeClassName;
	}

	/**
	 * 得到Hibernate模板对象类
	 * 
	 * @return Hibernate模板对象类
	 */
	protected HibernateTemplate getHibernateTemplate() {
		return this._template;
	}

	// 得到移动块的左边的起始位置,注意的是：查询的时候是 >=
	protected abstract int getMoveBlockLeftStart();

	// 得到移动块的左边的结束位置,注意的是：查询的时候是 <=
	protected abstract int getMoveBlockLeftEnd();

	// 得到当前节点移动的偏移量
	protected abstract int getOffset();

	// 得到受影响块更新的宽度
	protected abstract int getUpdateWidth();

	// 查询移动到的节点条件
	protected abstract void appendQueryReplaceNodeCriteria(
			DetachedCriteria criteria);

	// 获取移动模板的位置信息
	protected abstract void fetchMoveBlockInfo(List list);

	public void execute(Treeable node, HibernateTemplate template, int n, Class clazz) {

		this.node = node;
		this._template = template;

		if (clazz != null) {
			treeClassName = clazz.getName();
		} else {
			treeClassName = ClazzUtils.getEntityClass(node).getName();
		}
		// 对当前的node刷新
		template.refresh(node);
		// 查到移动到位置的节点
		List list = fetchReplaceNode(Math.abs(n) - 1);

		// 获取移动模块的位置信息
		fetchMoveBlockInfo(list);

		// 先孤立待移动的节点以及子节点
		insulateCurrentNodeAndChildren();

		// 更新影响到的块
		updateNodesAffected();
		// 更新当前的节点
		updateCurrentNode();

		// 刷新影响的节点
		template.refresh(node);

		if (list.size() > 0) {
			template.evict(list.get(0));
		}

	}

	/**
	 * 抓取将要替代的节点
	 * 
	 * @param n 移动的绝对距离
	 * @return 抓取的对象列表
	 */
	private List fetchReplaceNode(int n) {
		
		DetachedCriteria criteria = DetachedCriteria.forClass(ClazzUtils
				.getEntityClass(node));
		/*
		 * add restrictions in two ways:
		 * 1.move down see {@link MoveDownProcessor} 
		 * 2.move up see{@link MoveUpProcessor}
		 */
		appendQueryReplaceNodeCriteria(criteria);
		/*
		 * add restrictions:
		 * where depth = self.depth
		 */
		criteria.add(Restrictions.eq(Treeable.DEPTH_PRO_NAME, node.getDepth()));
		
		return _template.findByCriteria(criteria, n, 1);

	}

	// 孤立当前节点以及子节点
	private void insulateCurrentNodeAndChildren() {
		
		/*
		 * 将当前节点孤立:做法是把当前节点和其子节点的right和left同时变为相反数
		 * update table set left=0-left, right=0-right where (left between self.left and self.right); 
		 */
		executeUpdate(String.format(ISOLATE_NODE_HQL, treeClassName), node
				.getLeft(), node.getRight());
	}

	// 更新受影响的节点
	private void updateNodesAffected() {

		/*
		 * update table set left=left+updateWidth,right=right+updateWidth  where (left between blockLeftStart and blockLeftEnd)
		 */
		String updateNodesAffectedHQL = String.format(UPDATE_NODES_AFFECTED,
				treeClassName, getUpdateWidth(), getUpdateWidth());

		executeUpdate(updateNodesAffectedHQL, getMoveBlockLeftStart(),
				getMoveBlockLeftEnd());
	}

	private void executeUpdate(String hql, Object... parameters) {
		_template.bulkUpdate(hql,parameters);

	}

	// 更新当前的块
	private void updateCurrentNode() {

		/*
		 * update table set left=offset-left ,right=offset-right  where left<0  
		 */
		String updateCurrentNodeHQL = String.format(UPDATE_CURRENT_NODE_HQL,
				treeClassName, getOffset());

		executeUpdate(updateCurrentNodeHQL, 0);

	}

	/**
	 * 得到父节点
	 * 
	 * @return 父节点对象
	 */
	protected Treeable getParentNode() {
		if (this.parentNode != null) {
			return this.parentNode;
		}
		if (node.getDepth() > 1) {// 不为第一级的节点,则从库中获取
			DetachedCriteria criteria=DetachedCriteria.forEntityName(treeClassName);
			/**
			 * 条件为：上一级 右边大于当前的右边 左边小于当前的左边, 并且深度为当前的深度 - 1
			 */
			criteria.add(
					Restrictions.gt(Treeable.RIGHT_PRO_NAME, node.getRight())).add(
					Restrictions.eq(Treeable.DEPTH_PRO_NAME, node.getDepth() - 1))
					.add(Restrictions.lt(Treeable.LEFT_PRO_NAME, node
									.getLeft()));
			
			
			List list = _template.findByCriteria(criteria);

			if (list.size() != 1) { //
				throw new RuntimeException("非法请求，通过节点(" + node.getLeft() + ","
						+ node.getRight() + ")得到父节点为空");
			}
			parentNode = (Treeable) list.get(0);
		} else { // 为第一级节点,构造顶极根节点对象

			parentNode = constructRootNode();

		}
		return parentNode;
	}
	
	protected Treeable newTreeable(){
		return Treeable.class.cast(ClazzUtils.newInstance(getTreeClassName()));
	}

}